package com.financialDog.javaTool.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.financialDog.javaTool.common.PropertyConstant;
import com.financialDog.javaTool.reportModel.KeyValue;
import com.financialDog.javaTool.reportModel.ReportContext;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.xwintop.xcore.util.javafx.TooltipUtil;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;

import static javafx.geometry.Pos.TOP_CENTER;

public class AnalyticOlapUtils {

	public ReportContext reportContext;

	private HashSet<Integer> yearCollection;

	final String REFERENCE_KEY = "reference";

	public void initialOlap(){
		HashMap<String, Integer> balanceAccountMap = new HashMap<String, Integer>();
		reportContext = new ReportContext();
		yearCollection = new HashSet<Integer>();
		Integer accountSize = 0;  //指标数量
		//读取JSON
		String str = ResourceUtil.readUtf8Str("config/account.json");
		JSONObject configAccounts = JSONObject.parseObject(str);

		//获取资产负债表 资产
		JSONObject balanceObject = configAccounts.getJSONObject(PropertyConstant.BALANCE_SHEET_NAME);
		JSONArray balancePropertyArray = balanceObject.getJSONArray(PropertyConstant.BALANCE_PROPERTY); //资产
		int balancePropertySize = balancePropertyArray.size();
		int i = 0;
		for(;i< balancePropertySize; i++){
			JSONObject account =  balancePropertyArray.getJSONObject(i); //获取指标名称
			String name = account.getString(PropertyConstant.NAME_KEY);
			balanceAccountMap.put(name, accountSize++);
			//现在只有两层, 暂时不用递归
			if (account.containsKey(PropertyConstant.CHILDREN_KEY)){
				JSONArray accountChildren = account.getJSONArray(PropertyConstant.CHILDREN_KEY); //资产
				for(int j = 0; j< accountChildren.size(); j++){
					JSONObject accountChild =  accountChildren.getJSONObject(j); //获取指标名称
					String childName = accountChild.getString(PropertyConstant.NAME_KEY);
					balanceAccountMap.put(childName, accountSize++);
				}
			}
		}

		//获取资产负债表 负债
		JSONArray liabilitiesArray = balanceObject.getJSONArray("liabilities"); //资产
		int liabilitiesSize = liabilitiesArray.size();
		i = 0;
		for(;i< liabilitiesSize; i++){
			JSONObject account =  liabilitiesArray.getJSONObject(i); //获取指标名称
			String name = account.getString("name");
			balanceAccountMap.put(name, accountSize++);
			//现在只有两层, 暂时不用递归
			if (account.containsKey("children")){
				JSONArray accountChildren = account.getJSONArray("children"); //资产
				for(int j = 0; j < accountChildren.size(); j++){
					JSONObject accountChild =  accountChildren.getJSONObject(j); //获取指标名称
					String childName = accountChild.getString("name");
					balanceAccountMap.put(childName, accountSize++);
				}
			}
		}

		Integer balanceAccountSize = accountSize;

		//获取利润表
		JSONObject profitObject = configAccounts.getJSONObject("ProfitSheet");
		accountSize = generateAccount(balanceAccountMap, profitObject, accountSize, "income");
		accountSize = generateAccount(balanceAccountMap, profitObject, accountSize, "profit");
		accountSize = generateAccount(balanceAccountMap, profitObject, accountSize, "totalProfit");
		accountSize = generateAccount(balanceAccountMap, profitObject, accountSize, "pureIncome");
		accountSize = generateAccount(balanceAccountMap, profitObject, accountSize, "everyIncome");


		Integer moneyAccountSize = accountSize;

		//获取现金流量表
		JSONObject moneyObject = configAccounts.getJSONObject("MoneySheet");
		accountSize = generateAccount(balanceAccountMap, moneyObject, accountSize, "money");
		accountSize = generateAccount(balanceAccountMap, moneyObject, accountSize, "management");
		accountSize = generateAccount(balanceAccountMap, moneyObject, accountSize, "investment");
		accountSize = generateAccount(balanceAccountMap, moneyObject, accountSize, "financing");

		//初始化 context
		reportContext.initialContext(accountSize, balanceAccountMap, balanceAccountSize, moneyAccountSize);
	}

	private Integer generateAccount(HashMap<String, Integer> balanceAccountMap, JSONObject jsonObject,Integer accountSize ,String accountName) {
		int i = 0;
		JSONArray jsonArray = jsonObject.getJSONArray(accountName); //资产
		int size = jsonArray.size();
		for(;i< size; i++){
			JSONObject account =  jsonArray.getJSONObject(i); //获取指标名称
			String name = account.getString("name");
			balanceAccountMap.put(name, accountSize++);
			//现在只有两层, 暂时不用递归
			if (account.containsKey("children")){
				JSONArray accountChildren = account.getJSONArray("children"); //资产
				for(int j = 0; j < accountChildren.size(); j++){
					JSONObject accountChild =  accountChildren.getJSONObject(j); //获取指标名称
					String childName = accountChild.getString("name");
					balanceAccountMap.put(childName, accountSize++);
				}
			}
		}
		return accountSize;
	}


	public HashSet<Integer> getYearCollection() {
		return yearCollection;
	}

	public void addOlapData(String filePath, String tableType, String strYear){
		File file = FileUtil.file(filePath);
		if (file == null){
			TooltipUtil.showToast("文件不存在");
			return;
		}
		Integer year = Integer.valueOf(strYear);
		if (year < 2000 || year > 2030){
			//年份有问题
			TooltipUtil.showToast("年份选择有误");
			return ;
		}
		if (!yearCollection.contains(year)){
			yearCollection.add(year);
		}

		ExcelReader reader = ExcelUtil.getReader(file,"资产负债表");
		List<List<Object>> readAll = reader.read();
		List<Object> headList = readAll.get(3); //默认第四行是表格头部
		HashMap<String , Integer> accountIndexMap = new HashMap<String, Integer>();
		int index = 0;
		for (Object headString: headList) {
			String headTitle = headString.toString();
			headTitle = headTitle.replace(":", "").replace((char) 12288, ' ').replace(" ", "").trim();
			if (headTitle.equals("资产") || headTitle.equals("负债和所有者权益（或股东权益）")){
				accountIndexMap.put(headTitle, index);
			}
			index++;
		}

		//期末期初
		int dataIndex = 4;
		for(;dataIndex < readAll.size(); dataIndex++){
			//资产指标
			List<Object> dataline = readAll.get(dataIndex);
			Integer propertyIndex = accountIndexMap.get("资产");
			String strAccount = dataline.get(propertyIndex).toString().trim();
			strAccount = getAccountName(strAccount, false);


			BigDecimal startNumber = getDataFromDataLine(dataline.get(propertyIndex + 1).toString());
			BigDecimal endNumber = getDataFromDataLine(dataline.get(propertyIndex + 2).toString());
			addData(strAccount, "start", "balanceTable", year, "Y", startNumber);
			addData(strAccount, "end", "balanceTable", year, "Y", endNumber);

			//负债指标
			Integer liabilitiesIndex = accountIndexMap.get("负债和所有者权益（或股东权益）");
			strAccount = dataline.get(liabilitiesIndex).toString();
			strAccount = getAccountName(strAccount, false);

			endNumber = getDataFromDataLine(dataline.get(liabilitiesIndex + 1).toString());
			startNumber = getDataFromDataLine(dataline.get(liabilitiesIndex + 2).toString());
			addData(strAccount, "start", "balanceTable", year, "Y", startNumber);
			addData(strAccount, "end", "balanceTable", year, "Y", endNumber);
		}

		//利润表
		addProfitTableData(file, year);

		//现金流量表
		addMoneyTableData(file, year);

		int a = 2;
	}

	//获取指标
	private String getAccountName(String accountName, boolean isProfit){
		if (isProfit){
			//利润表要特殊处理
			accountName = accountName.replace("其中：","")
					.replace("加：","")
					.replace("减：","")
					.replace("*","");
			accountName = clearBracket(accountName, '（', '）');

		}
		accountName = accountName.replace("：","")
						.replace("一、", "")
						.replace("二、", "")
						.replace("三、", "")
				        .replace((char) 12288, ' ')
				        .replace(" ", "").trim();;

		if (isProfit){
			//利润表要特殊 处理
			return accountName;
		}
		return accountName;
	}

	private static String clearBracket(String context, char left, char right) {
		int head = context.indexOf(left);
		if (head == -1) {
			return context;
		} else {
			int next = head + 1;
			int count = 1;
			do {
				if (context.charAt(next) == left) {
					count++;
				} else if (context.charAt(next) == right) {
					count--;
				}
				next++;
				if (count == 0) {
					String temp = context.substring(head, next);
					context = context.replace(temp, "");
					head = context.indexOf(left);
					next = head + 1;
					count = 1;
				}
			} while (head != -1);
		}
		return context;
	}


	private void addProfitTableData(File file, Integer year) {
		ExcelReader profitReader = ExcelUtil.getReader(file, "利润表");
		List<List<Object>> profitReadAll = profitReader.read();
		List<Object> profitHeadList = profitReadAll.get(3); //默认第四行是表格头部
		HashMap<String, Integer> profitAccountIndexMap = new HashMap<String, Integer>();
		Integer profitIndex = 0;
		for (Object headString : profitHeadList) {
			String headTitle = headString.toString();
			headTitle = headTitle.replace(":", "").replace((char) 12288, ' ').replace(" ", "").trim();
			if (headTitle.equals("项目")) {
				profitAccountIndexMap.put(headTitle, profitIndex);
			}
			profitIndex++;
		}

		int profitDataIndex = 4;
		for (; profitDataIndex < profitReadAll.size(); profitDataIndex++) {
			//项目指标
			List<Object> dataline = profitReadAll.get(profitDataIndex);
			Integer projectIndex = profitAccountIndexMap.get("项目");
			String strAccount = dataline.get(projectIndex).toString().trim();
			strAccount = getAccountName(strAccount, true);
			BigDecimal currentAmount = getDataFromDataLine(dataline.get(projectIndex + 1).toString());
			BigDecimal yearAmount = getDataFromDataLine(dataline.get(projectIndex + 2).toString());
			addData(strAccount, "currentAmount", "profitTable", year, "Y", currentAmount);
			addData(strAccount, "yearAmount", "profitTable", year, "Y", yearAmount);
		}
	}


	private void addMoneyTableData(File file, Integer year) {
		ExcelReader excelReader = ExcelUtil.getReader(file, "现金流量表");
		List<List<Object>> moneyReadAll = excelReader.read();
		List<Object> headList = moneyReadAll.get(3); //默认第四行是表格头部
		HashMap<String, Integer> profitAccountIndexMap = new HashMap<String, Integer>();
		Integer profitIndex = 0;
		for (Object headString : headList) {
			String headTitle = headString.toString();
			headTitle = headTitle.replace(":", "").replace((char) 12288, ' ').replace(" ", "").trim();
			if (headTitle.equals("项目")) {
				profitAccountIndexMap.put(headTitle, profitIndex);
			}
			profitIndex++;
		}

		int dataIndex = 4;
		for (; dataIndex < moneyReadAll.size() ; dataIndex++) {
			//项目指标
			List<Object> dataline = moneyReadAll.get(dataIndex);
			Integer projectIndex = profitAccountIndexMap.get("项目");
			String strAccount = dataline.get(projectIndex).toString().trim();
			strAccount = getAccountName(strAccount, false);
			BigDecimal currentAmount = getDataFromDataLine(dataline.get(projectIndex + 1).toString());
			BigDecimal yearAmount = getDataFromDataLine(dataline.get(projectIndex + 2).toString());
			addData(strAccount, "currentAmount", "moneyTable", year, "Y", currentAmount);
			addData(strAccount, "yearAmount", "moneyTable", year, "Y", yearAmount);
		}
	}


	public BigDecimal getDataFromDataLine(String dataValue) {
		if ( dataValue == null || StringUtils.isEmpty(dataValue)) {
			return BigDecimal.ZERO;
		}
		BigDecimal result = BigDecimal.ZERO;
		try {
			result = new BigDecimal(dataValue).setScale(2, BigDecimal.ROUND_HALF_UP);
		}catch (Exception e){
			result =  BigDecimal.ZERO;
		}
		return result;
	}

	public void addData(String account, String project, String tableType, Integer year, String time, BigDecimal data){
		Integer key = generatePosition(account, project, tableType, year, time);
		reportContext.addData(key, data);
	}

	public BigDecimal getData(String account, String project, String tableType, Integer year, String time){
		Integer key = generatePosition(account, project, tableType, year, time);
		return  reportContext.getData(key);
	}

	public BigDecimal getCalculateAccount(String account, Integer year){
		PropertyConstant.TABLE_TYPE tableType = reportContext.getTableTypeByAccount(account);
		String project = reportContext.getCalculateProjectByTableType(tableType);
		String tableName = reportContext.getTableNameByTableType(tableType);
		Integer key = generatePosition(account, project, tableName, year, PropertyConstant.TIME_YEAR);
		return  reportContext.getData(key);
	}

	public Integer generatePosition(String account, String project, String tableType, Integer year, String time){
		Integer accountNumber = reportContext.getBalanceAccountPosition(account);
		Integer projectPosition = reportContext.getProjectPosition(project);
		Integer timePosition = reportContext.getTimePosition(time);
		Integer tablePosition = reportContext.getTablePosition(tableType);
		Integer yearPosition = reportContext.getYearPosition(year);
		//生成key
		Integer projectNumber = projectPosition << reportContext.accountBinarySize;
		Integer timeNumber = timePosition << (reportContext.accountBinarySize + reportContext.projectSize);
		Integer yearNumber = yearPosition << (reportContext.accountBinarySize + reportContext.projectSize + reportContext.timeBinarySize);
		Integer tableNumber = tablePosition << (reportContext.accountBinarySize + reportContext.projectSize + reportContext.timeBinarySize + reportContext.yearBinarySize);

		//维度过多的时候要切换成StringKEY 防止溢出
		return accountNumber + projectNumber + timeNumber + yearNumber + tableNumber;
	}

	public void resolveExcelToMakeUpModel(String model, String filePath){
		File file = FileUtil.file(filePath);
		if (file == null){
			TooltipUtil.showToast("文件不存在");
			return;
		}
		ExcelReader reader = ExcelUtil.getReader(file,"资产负债表");
		List<List<Object>> readAll = reader.read();
		List<Object> headList = readAll.get(3);
	}


	public ObservableList<Map<String, String>> generateMergeBalanceReport() {
		ObservableList<Map<String, String>> tableData = FXCollections.observableArrayList();
		//获取资产负债表 资产
		String str = ResourceUtil.readUtf8Str("config/account.json");
		JSONObject configAccounts = JSONObject.parseObject(str);
		JSONObject balanceObject = configAccounts.getJSONObject("BalanceSheet");
		JSONArray balancePropertyArray = balanceObject.getJSONArray("property"); //资产
		JSONArray liabilitiesArray = balanceObject.getJSONArray("liabilities"); //负债
		int balancePropertySize = balancePropertyArray.size();
		int liabilitiesSize = liabilitiesArray.size();
		int i = 0;
		HashMap<Integer, BigDecimal> balanceTotalDataCollection = new HashMap<Integer, BigDecimal>();
		HashMap<Integer, BigDecimal> liabilitiesTotalDataCollection = new HashMap<Integer, BigDecimal>();
		//先把合计算出来 暂时不进行多线程同时计算

		for (; i < balancePropertySize; i++) {
			JSONObject account = balancePropertyArray.getJSONObject(i);
			if (account.containsKey("isTotal")){
				String name = account.getString("name");
				for (Integer year : yearCollection) {
					BigDecimal data = getData(name, "end", "balanceTable", year, "Y");
					balanceTotalDataCollection.put(year, data);
				}
			}
		}

		i = 0;
		for (; i < liabilitiesSize; i++) {
			JSONObject account = liabilitiesArray.getJSONObject(i);
			if (account.containsKey("isTotal")){
				String name = account.getString("name");
				for (Integer year : yearCollection) {
					BigDecimal data = getData(name, "end", "balanceTable", year, "Y");
					liabilitiesTotalDataCollection.put(year, data);
				}
			}
		}

		i = 0;
		for (; i < balancePropertySize; i++) {
			JSONObject account = balancePropertyArray.getJSONObject(i); //获取指标名称
			addTableLine( tableData, balanceTotalDataCollection, account);
			if (account.containsKey("children")) {
				JSONArray accountChildren = account.getJSONArray("children"); //资产
				for (int j = 0; j < accountChildren.size(); j++) {
					JSONObject accountChild = accountChildren.getJSONObject(j); //获取指标名称
					addTableLine(tableData, balanceTotalDataCollection, accountChild, true);
				}
			}
		}

		i = 0;
		for (; i < liabilitiesSize; i++) {
			JSONObject account = liabilitiesArray.getJSONObject(i); //获取指标名称
			addTableLine(tableData, liabilitiesTotalDataCollection, account);
			if (account.containsKey("children")) {
				JSONArray accountChildren = account.getJSONArray("children"); //负债
				for (int j = 0; j < accountChildren.size(); j++) {
					JSONObject accountChild = accountChildren.getJSONObject(j); //获取指标名称
					addTableLine( tableData, liabilitiesTotalDataCollection, accountChild, true);
				}
			}
		}

		return tableData;
	}

	public ObservableList<Map<String, String>> generateMergeProfitReport() {
		ObservableList<Map<String, String>> tableData = FXCollections.observableArrayList();
		//获取资产负债表 资产
		String str = ResourceUtil.readUtf8Str("config/account.json");
		JSONObject configAccounts = JSONObject.parseObject(str);
		JSONObject jsonObject = configAccounts.getJSONObject("ProfitSheet");
		JSONArray incomeArray = jsonObject.getJSONArray("income"); //收入
		JSONArray profitArray = jsonObject.getJSONArray("profit"); //负债
		JSONArray totalProfitArray = jsonObject.getJSONArray("totalProfit"); //负债
		JSONArray purIncomeArray = jsonObject.getJSONArray("pureIncome"); //负债
		JSONArray everyIncomeArray = jsonObject.getJSONArray("everyIncome"); //负债
		int incomeSize = incomeArray.size();
		int i = 0;
		HashMap<Integer, BigDecimal> profitTotalDataCollection = new HashMap<Integer, BigDecimal>();
		//先把合计算出来 暂时不进行多线程同时计算
		for (; i < incomeSize; i++) {
			JSONObject account = incomeArray.getJSONObject(i);
			if (account.containsKey("isTotal")){
				String name = account.getString("name");
				for (Integer year : yearCollection) {
					BigDecimal data = getData(name, "yearAmount", "profitTable", year, "Y");
					profitTotalDataCollection.put(year, data);
				}
			}
		}
		generateTableData( incomeArray, tableData, profitTotalDataCollection);
		generateTableData( profitArray, tableData, profitTotalDataCollection);
		generateTableData( totalProfitArray, tableData, profitTotalDataCollection);
		generateTableData( purIncomeArray, tableData, profitTotalDataCollection);
		generateTableData( everyIncomeArray, tableData, profitTotalDataCollection);

		return tableData;
	}

	public ObservableList<Map<String, String>> generateMergeMoneyReport() {
		ObservableList<Map<String, String>> tableData = FXCollections.observableArrayList();
		// 现金流量表
		String str = ResourceUtil.readUtf8Str("config/account.json");
		JSONObject configAccounts = JSONObject.parseObject(str);
		JSONObject jsonObject = configAccounts.getJSONObject("MoneySheet");
		JSONArray  jsonArray = new JSONArray();
		int size = jsonArray.size();
		int i = 0;
		HashMap<Integer, BigDecimal> moneyTotalDataCollection = new HashMap<Integer, BigDecimal>();
		jsonObject.keySet().forEach(key->{
			jsonArray.addAll(jsonObject.getJSONArray(key)); //现金流量
		});
		//先把合计算出来 暂时不进行多线程同时计算
		generateMoneyTableData(jsonArray, tableData, moneyTotalDataCollection);
		return tableData;
	}

	private void generateMoneyTableData(JSONArray accountArray,ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> totalDataCollection) {
		int i = 0 ;
		for (; i < accountArray.size(); i++) {
			JSONObject account = accountArray.getJSONObject(i); //获取指标名称
			addMoneyTableLine(tableData, totalDataCollection, account);
			if (account.containsKey(PropertyConstant.CHILDREN_KEY)) {
				JSONArray accountChildren = account.getJSONArray(PropertyConstant.CHILDREN_KEY); //资产
				for (int j = 0; j < accountChildren.size(); j++) {
					JSONObject accountChild = accountChildren.getJSONObject(j); //获取指标名称
					addMoneyTableLine(tableData, totalDataCollection, accountChild, true);
				}
			}
		}
	}


	private void generateTableData(JSONArray accountArray,ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> totalDataCollection) {
		int i = 0 ;
		for (; i < accountArray.size(); i++) {
			JSONObject account = accountArray.getJSONObject(i); //获取指标名称
			addProfitTableLine(tableData, totalDataCollection, account);
			if (account.containsKey("children")) {
				JSONArray accountChildren = account.getJSONArray("children"); //资产
				for (int j = 0; j < accountChildren.size(); j++) {
					JSONObject accountChild = accountChildren.getJSONObject(j); //获取指标名称
					addProfitTableLine(tableData, totalDataCollection, accountChild, true);
				}
			}
		}
	}

	//暂时区分开来。以后应该要进行不同的合并
	private void addTableLine( ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account) {
		addTableLine(tableData , balanceTotalDataCollection, account, false);
	}

	private void addTableLine(ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account, boolean isChild){
		String name = account.getString("name");
		//根据年份 和  指标名称 获取数据填入到
		HashMap<String, String> resultMap = new HashMap<String, String>();
		List<BigDecimal> lastTwoYearData = new ArrayList<BigDecimal>();
		lastTwoYearData.add(BigDecimal.ZERO);
		lastTwoYearData.add(BigDecimal.ZERO);
		for (Integer year : yearCollection) {
			BigDecimal data = getData(name, "end", "balanceTable", year, "Y");
			if (data == null){
				continue;
			}
			resultMap.put("合并报表" + year , data.toString());
			BigDecimal total = balanceTotalDataCollection.get(year);
			BigDecimal percentage = data.multiply(BigDecimal.valueOf(100)).divide(total, 2).setScale(2, BigDecimal.ROUND_HALF_UP);
			resultMap.put("占比" + year, percentage.toString() + "%");
			lastTwoYearData.set(1, lastTwoYearData.get(0));
			lastTwoYearData.set(0, data);
		}
		BigDecimal balanceValue = lastTwoYearData.get(0).subtract(lastTwoYearData.get(1)).setScale(2);
		if (lastTwoYearData.get(1).compareTo(BigDecimal.ZERO) != 0){
			BigDecimal chainValue = balanceValue.multiply(BigDecimal.valueOf(100)).divide(lastTwoYearData.get(1), 2).setScale(2, BigDecimal.ROUND_HALF_UP);
			resultMap.put("环比", chainValue.toString() + "%");
		}else{
			resultMap.put("环比", "-");
		}
		resultMap.put("差额", balanceValue.toString());
		if (isChild){
			name = "   " + name;
		}
		resultMap.put("报表类型", name);
		tableData.add(resultMap);
	}

	//暂时区分开来。以后应该要进行不同的合并
	private void addProfitTableLine( ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account) {
		addProfitTableLine(tableData , balanceTotalDataCollection, account, false);
	}

	private void addProfitTableLine(ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account, boolean isChild){
		String name = account.getString("name");
		//根据年份 和  指标名称 获取数据填入到
		HashMap<String, String> resultMap = new HashMap<String, String>();
		List<BigDecimal> lastTwoYearData = new ArrayList<BigDecimal>();
		lastTwoYearData.add(BigDecimal.ZERO);
		lastTwoYearData.add(BigDecimal.ZERO);
		for (Integer year : yearCollection) {
			BigDecimal data = getData(name, "yearAmount", "profitTable", year, "Y");
			if (data == null){
				continue;
			}
			resultMap.put("合并报表" + year , data.toString());
			BigDecimal total = balanceTotalDataCollection.get(year);
			BigDecimal percentage = data.multiply(BigDecimal.valueOf(100)).divide(total, 2).setScale(2, BigDecimal.ROUND_HALF_UP);
			resultMap.put("占比" + year, percentage.toString() + "%");
			lastTwoYearData.set(1, lastTwoYearData.get(0));
			lastTwoYearData.set(0, data);
		}
		BigDecimal balanceValue = lastTwoYearData.get(0).subtract(lastTwoYearData.get(1)).setScale(2);
		if (lastTwoYearData.get(1).compareTo(BigDecimal.ZERO) != 0){
			BigDecimal chainValue = balanceValue.multiply(BigDecimal.valueOf(100)).divide(lastTwoYearData.get(1), 2).setScale(2, BigDecimal.ROUND_HALF_UP);
			resultMap.put("环比", chainValue.toString() + "%");
		}else{
			resultMap.put("环比", "-");
		}
		resultMap.put("差额", balanceValue.toString());
		if (isChild){
			name = "   " + name;
		}
		resultMap.put("报表类型", name);
		tableData.add(resultMap);
	}


	//暂时区分开来。以后应该要进行不同的合并
	private void addMoneyTableLine( ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account) {
		addMoneyTableLine(tableData , balanceTotalDataCollection, account, false);
	}

	private void addMoneyTableLine(ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account, boolean isChild){
		String name = account.getString("name");
		//根据年份 和  指标名称 获取数据填入到
		HashMap<String, String> resultMap = new HashMap<String, String>();
		List<BigDecimal> lastTwoYearData = new ArrayList<BigDecimal>();
		lastTwoYearData.add(BigDecimal.ZERO);
		lastTwoYearData.add(BigDecimal.ZERO);
		for (Integer year : yearCollection) {
			BigDecimal data = getData(name, "yearAmount", "moneyTable", year, "Y");
			if (data == null){
				continue;
			}
			resultMap.put("合并报表" + year , data.toString());
			BigDecimal total = balanceTotalDataCollection.get(year);
//			BigDecimal percentage = data.multiply(BigDecimal.valueOf(100)).divide(total, 2).setScale(2, BigDecimal.ROUND_HALF_UP);
//			resultMap.put("占比" + year, percentage.toString() + "%");
			lastTwoYearData.set(1, lastTwoYearData.get(0));
			lastTwoYearData.set(0, data);
		}
		BigDecimal balanceValue = lastTwoYearData.get(0).subtract(lastTwoYearData.get(1)).setScale(2, BigDecimal.ROUND_HALF_UP);
		if (lastTwoYearData.get(1).compareTo(BigDecimal.ZERO) != 0){
			BigDecimal chainValue = balanceValue.multiply(BigDecimal.valueOf(100)).divide(lastTwoYearData.get(1), 2).setScale(2, BigDecimal.ROUND_HALF_UP);
			resultMap.put("环比", chainValue.toString() + "%");
		}else{
			resultMap.put("环比", "-");
		}
		resultMap.put("差额", balanceValue.toString());
		if (isChild){
			name = "   " + name;
		}
		resultMap.put("报表类型", name);
		tableData.add(resultMap);
	}

	public void validateBalanceData(){
		HashSet<Integer> yearCollection = getYearCollection();

		// 规则1： 子指标的相加 应该要等于父指标 父指标如果不是指标 就要进行校验
		//遍历指标
		JSONObject accountObject = getConfigAccountByType(PropertyConstant.TABLE_TYPE.BALANCE_TABLE);
		JSONArray accountArray = accountObject.getJSONArray("property");
		boolean isAllRight = true;
		for(int i = 0; i < accountArray.size(); i++){
			JSONObject account = accountArray.getJSONObject(i);
			String name = account.getString("name");
			String actualName = name;
			String referenceName = StringUtils.EMPTY;
			boolean isSum = false;
			if (account.containsKey("sum")){
				isSum = account.getBoolean("sum");
			}
			if(!isSum){
				continue;
			}

			if (account.containsKey(REFERENCE_KEY)){
				referenceName = account.getString(REFERENCE_KEY);
			}

			HashMap<Integer, BigDecimal> totalDataCollection = new HashMap<>();
			if (StringUtils.isNotEmpty(referenceName)){
				name = referenceName;
			}
			for (Integer year: yearCollection ){
				BigDecimal totalData = getData(name, "end", "balanceTable", year, "Y");
				totalDataCollection.put(year, totalData);
			}

			if (account.containsKey("children")){
				JSONArray accountChildren = account.getJSONArray("children"); //资产
				HashMap<Integer, BigDecimal> sumDataCollection = new HashMap<>();
				//先获取总值
				for(int j = 0; j< accountChildren.size(); j++){
					JSONObject accountChild =  accountChildren.getJSONObject(j); //获取指标名称
					String childName = accountChild.getString("name");
					for (Integer year: yearCollection ){
						BigDecimal data = getData(childName, "end", "balanceTable", year, "Y");
						BigDecimal sumData = sumDataCollection.get(year);
						if (sumData == null){
							sumData = BigDecimal.ZERO;
						}
						sumData = sumData.add(data).setScale(2, BigDecimal.ROUND_HALF_UP);
						sumDataCollection.put(year, sumData);
					}
				}

				//进行校验
				for (Integer year: yearCollection) {
					BigDecimal totalData = totalDataCollection.get(year);
					BigDecimal sumData = sumDataCollection.get(year);
					if (totalData.compareTo(sumData) != 0){
						TooltipUtil.showToast(actualName + ":检验失败");
						isAllRight = false;
					}
				}
			}
		}

		if (isAllRight){
			TooltipUtil.showToast("检验结果","资产负债表" + ":数据检验通过", TOP_CENTER);
		}else{
			TooltipUtil.showToast("检验结果","数据有错误" + ":检验失败", TOP_CENTER);
		}
	}

	public void validateProfitData(){
		HashSet<Integer> yearCollection = getYearCollection();

		// 规则1： 子指标的相加 应该要等于父指标 父指标如果不是指标 就要进行校验
		//遍历指标
		JSONObject accountObject = getConfigAccountByType(PropertyConstant.TABLE_TYPE.PROFIT_TABLE);
		boolean isAllRight = validateProfitNode(yearCollection, accountObject);

		if (isAllRight){
			TooltipUtil.showToast("检验结果","利润表" + ":数据检验通过", TOP_CENTER);
		}else{
			TooltipUtil.showToast("检验结果","数据有错误" + ":检验失败", TOP_CENTER);
		}
	}

	public void validateMoneyData(){
		JSONObject accountObject = getConfigAccountByType(PropertyConstant.TABLE_TYPE.MONEY_TABLE);
		boolean isAllRight = validateMoneyNode(yearCollection, accountObject);
		if (isAllRight){
			TooltipUtil.showToast("检验结果","现金流量表" + ":数据检验通过", TOP_CENTER);
		}else{
			TooltipUtil.showToast("检验结果","数据有错误" + ":检验失败", TOP_CENTER);
		}
	}

	private boolean validateMoneyNode(HashSet<Integer> yearCollection, JSONObject accountObject) {
		boolean isAllRight = true;
		for (Integer year: yearCollection){
			JSONArray managementArray = accountObject.getJSONArray(PropertyConstant.MONEY_ACCOUNT_MANAGEMENT);
			isAllRight = validateMoneyNode(year, managementArray,
					"经营活动现金流入小计",
					"经营活动现金流出小计",
					"经营活动产生的现金流量净额");


			JSONArray investmentArray = accountObject.getJSONArray(PropertyConstant.MONEY_ACCOUNT_INVESTMENT);
			isAllRight = validateMoneyNode(year, investmentArray,
					"投资活动现金流入小计",
					"投资活动现金流出小计",
					"投资活动产生的现金流量净额");


			JSONArray financingArray = accountObject.getJSONArray(PropertyConstant.MONEY_ACCOUNT_FINANCING);
			isAllRight = validateMoneyNode(year, financingArray,
					"筹资活动现金流入小计",
					"筹资活动现金流出小计",
					"筹资活动产生的现金流量净额");
		}
		return isAllRight;
	}

	private boolean validateMoneyNode(Integer year, JSONArray managementArray, String inAccountName, String outAccountName, String totalAccountName) {
		boolean isAllRight = true;
		for (int i = 0; i < managementArray.size(); i++) {
			JSONObject account = managementArray.getJSONObject(i);
			String name = account.getString(PropertyConstant.NAME_KEY);
			BigDecimal inManageAmount = getData(inAccountName,
					PropertyConstant.MONEY_PROJECT_YEAR_AMOUNT,
					PropertyConstant.MONEY_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR);

			if (name.equals(inAccountName)) {
				//计算合计
				BigDecimal totalData = BigDecimal.ZERO;
				JSONArray children = account.getJSONArray(PropertyConstant.CHILDREN_KEY);
				for (int j = 0; j < children.size(); j++) {
					JSONObject childObject = children.getJSONObject(j);
					BigDecimal data = getData(childObject.getString(PropertyConstant.NAME_KEY),
							PropertyConstant.MONEY_PROJECT_YEAR_AMOUNT,
							PropertyConstant.MONEY_TABLE_NAME,
							year,
							PropertyConstant.TIME_YEAR);
					totalData = totalData.add(data);
				}

				if (totalData.compareTo(inManageAmount) != 0) {
					isAllRight = false;
					TooltipUtil.showToast("检验结果", "现金流量表" + ":数据检验失败 规则1失败", TOP_CENTER);
				}

			}

			BigDecimal outManageAmount = getData(outAccountName,
					PropertyConstant.MONEY_PROJECT_YEAR_AMOUNT,
					PropertyConstant.MONEY_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR);
			if (name.equals(outAccountName)) {
				//计算合计

				BigDecimal totalData = BigDecimal.ZERO;
				JSONArray children = account.getJSONArray(PropertyConstant.CHILDREN_KEY);
				for (int j = 0; j < children.size(); j++) {
					JSONObject childObject = children.getJSONObject(j);
					BigDecimal data = getData(childObject.getString(PropertyConstant.NAME_KEY),
							PropertyConstant.MONEY_PROJECT_YEAR_AMOUNT,
							PropertyConstant.MONEY_TABLE_NAME,
							year,
							PropertyConstant.TIME_YEAR);
					totalData = totalData.add(data);
				}

				if (totalData.compareTo(outManageAmount) != 0) {
					isAllRight = false;
					TooltipUtil.showToast("检验结果", "现金流量表" + ":数据检验失败 规则2失败", TOP_CENTER);
				}
			}

			BigDecimal pureAmount = getData(totalAccountName,
					PropertyConstant.MONEY_PROJECT_YEAR_AMOUNT,
					PropertyConstant.MONEY_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR);

			BigDecimal result1 = inManageAmount.subtract(outManageAmount);
			if (result1.compareTo(pureAmount) != 0) {
				isAllRight = false;
				TooltipUtil.showToast("检验结果", "现金流量表" + ":数据检验失败 规则3失败", TOP_CENTER);
			}
		}
		return isAllRight;
	}
	

	private boolean validateProfitNode(HashSet<Integer> yearCollection, JSONObject accountObject) {
		boolean isAllRight = true;
		for (Integer year: yearCollection){

			JSONArray inComeArray = accountObject.getJSONArray(PropertyConstant.PROFIT_ACCOUNT_INCOME);
			JSONObject inCome = inComeArray.getJSONObject(0);
			JSONArray childArray = inCome.getJSONArray(PropertyConstant.CHILDREN_KEY);
			String inComeName = inCome.getString(PropertyConstant.NAME_KEY);
			BigDecimal inComeAmount = getData(inComeName,
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR);
			BigDecimal totalData = BigDecimal.ZERO;
			for (int i = 0; i < childArray.size(); i++ ){
				JSONObject accountChild =  childArray.getJSONObject(i); //获取指标名称
				String childName = accountChild.getString(PropertyConstant.NAME_KEY);
				BigDecimal data = getData(childName,
						PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
						PropertyConstant.PROFIT_TABLE_NAME,
						year,
						PropertyConstant.TIME_YEAR);
				totalData = totalData.add(data);
			}

			BigDecimal totalProfitAmount = getData("利润总额",
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR );
			BigDecimal profit = getData("营业利润",
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR );
			BigDecimal outAmount = getData("营业外支出",
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR );

			BigDecimal inOutAmount = getData("营业外收入",
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR );

			BigDecimal pureProfitAmount = getData("净利润",
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR );

			BigDecimal taxAmount = getData("所得税费用",
					PropertyConstant.PROFIT_PROJECT_YEAR_AMOUNT,
					PropertyConstant.PROFIT_TABLE_NAME,
					year,
					PropertyConstant.TIME_YEAR );
			// 规则1 : 营业利润 = 营业收入-营业成本-营业税金及附加-销售费用-管理费用-财务费用-资产减值损失-/+公允价值变动损益-/+投资收益等
			BigDecimal result1 = inComeAmount.subtract(totalData);
			if (result1.compareTo(profit) != 0){
				isAllRight =  false;
				TooltipUtil.showToast("检验结果","利润表" + ":数据检验失败 规则1失败", TOP_CENTER);
			}

			// 规则2 :利润总额=营业利润+营业外收入-营业外支出
			BigDecimal result2 = profit.add(inOutAmount).subtract(outAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
			if (result2.compareTo(totalProfitAmount) != 0){
				isAllRight =  false;
				TooltipUtil.showToast("检验结果","利润表" + ":数据检验失败 规则2失败", TOP_CENTER);
			}

			// 规则3 : 净利润=利润总额-所得税费用
			BigDecimal result3 =  totalProfitAmount.subtract(taxAmount);
			if (result3.compareTo(pureProfitAmount) != 0){
				isAllRight =  false;
				TooltipUtil.showToast("检验结果","利润表" + ":数据检验失败 规则3失败", TOP_CENTER);
			}

		}
		return isAllRight;
	}


	public JSONObject getConfigAccountByType(PropertyConstant.TABLE_TYPE tableType){
		String str = ResourceUtil.readUtf8Str("config/account.json");
		JSONObject configAccounts = JSONObject.parseObject(str);

		//获取资产负债表 资产
		if (tableType.equals(PropertyConstant.TABLE_TYPE.BALANCE_TABLE)){
			return configAccounts.getJSONObject("BalanceSheet");
		}

		//获取利润表
		if (tableType.equals(PropertyConstant.TABLE_TYPE.PROFIT_TABLE)){
			return configAccounts.getJSONObject("ProfitSheet");
		}

		//获取利润表
		if (tableType.equals(PropertyConstant.TABLE_TYPE.MONEY_TABLE)){
			return configAccounts.getJSONObject("MoneySheet");
		}

		return null;
	}


	//初始化 计算引擎
	public void initialCalculate(String key, IExpressContext<String, Object> context, Integer year){
		String str = ResourceUtil.readUtf8Str("config/calculate_account.json");
		JSONObject accountObject = JSONObject.parseObject(str);
		if (!accountObject.containsKey(key)){
			return;
		}
		JSONObject keyObject = accountObject.getJSONObject(key);
		JSONArray containArray = keyObject.getJSONArray(PropertyConstant.CONTAIN_ACCOUNT_KEY);
		for(int i = 0; i < containArray.size(); i ++){
			String accountName = containArray.getString(i);
			BigDecimal value = getCalculateAccount(accountName, year);
			context.put(accountName, value);
		}
	}

	public BigDecimal calculateFormula(String key ,String formula, Integer year){
		ExpressRunner runner = new ExpressRunner();
		IExpressContext<String, Object> context = new DefaultContext<String, Object>();
		initialCalculate(key , context, year);
		try {
			Object result = runner.execute(formula, context, null, false, true);
			return new BigDecimal(result.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return BigDecimal.ZERO;
	}

	public ObservableList<Map<String, String>> generateAccountReport(HashMap<KeyValue, String > accountData) {
		ObservableList<Map<String, String>> tableData = FXCollections.observableArrayList();
		//先把合计算出来 暂时不进行多线程同时计算
		accountData.forEach((k, v)->{
   			HashMap<String, String> resultMap = new HashMap<String, String>();
			resultMap.put("指标", k.getValue());
			for (Integer year : yearCollection) {
				BigDecimal dataValue = calculateFormula(k.getKey(), v, year);
				resultMap.put(year.toString(), dataValue.toString());
			}
			tableData.add(resultMap);
		});

		return tableData;
	}

	private void addAccountTableLine(ObservableList<Map<String, String>> tableData, HashMap<Integer, BigDecimal> balanceTotalDataCollection, JSONObject account){
		String name = account.getString("name");
		//根据年份 和  指标名称 获取数据填入到
		HashMap<String, String> resultMap = new HashMap<String, String>();
		List<BigDecimal> lastTwoYearData = new ArrayList<BigDecimal>();
		lastTwoYearData.add(BigDecimal.ZERO);
		lastTwoYearData.add(BigDecimal.ZERO);
		for (Integer year : yearCollection) {
			BigDecimal data = getData(name, "yearAmount", "moneyTable", year, "Y");
			resultMap.put("合并报表" + year , data.toString());
			BigDecimal total = balanceTotalDataCollection.get(year);
//			BigDecimal percentage = data.multiply(BigDecimal.valueOf(100)).divide(total, 2).setScale(2, BigDecimal.ROUND_HALF_UP);
//			resultMap.put("占比" + year, percentage.toString() + "%");
			lastTwoYearData.set(1, lastTwoYearData.get(0));
			lastTwoYearData.set(0, data);
		}
		BigDecimal balanceValue = lastTwoYearData.get(0).subtract(lastTwoYearData.get(1)).setScale(2, BigDecimal.ROUND_HALF_UP);
		if (lastTwoYearData.get(1).compareTo(BigDecimal.ZERO) != 0){
			BigDecimal chainValue = balanceValue.multiply(BigDecimal.valueOf(100)).divide(lastTwoYearData.get(1), 2).setScale(2, BigDecimal.ROUND_HALF_UP);
			resultMap.put("环比", chainValue.toString() + "%");
		}else{
			resultMap.put("环比", "-");
		}
		resultMap.put("差额", balanceValue.toString());
		resultMap.put("报表类型", name);
		tableData.add(resultMap);
	}
}
